package org.deltaset.faces;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;

/**
 *
 * @author dmakariev
 */
public final class PageHelper<T> implements Serializable {

    //TODO: must introduce Builder here.. 
    public static <T> PageHelper<T> newInstance(final Finder<T> finder) {
        return new PageHelper<T>(finder);
    }

    private PageHelper(final Finder<T> finder) {
        this(finder, DEFAULT_MAX_ITEMS_ON_PAGE, DEFAULT_CURRENT_PAGE, new Object[0]);
    }

    private PageHelper(final Finder<T> finder, final int maxItemsOnPage, final long currentPage, final Object[] params) {
        this.maxItemsOnPage = maxItemsOnPage;
        this.finder = finder;
        this.currentPage = currentPage;
        this.params = params;
        this.pageInfo = initPageInfo(finder, params, currentPage, maxItemsOnPage);
        this.items = initItems(finder, this.pageInfo, params);
    }
    private final static int DEFAULT_MAX_ITEMS_ON_PAGE = 5;
    private final static long DEFAULT_CURRENT_PAGE = 1;
    private final Finder<T> finder;
    private final int maxItemsOnPage;
    private final long currentPage;
    private final PageInfo pageInfo;
    private final Object[] params;
    private final List<T> items;

     public PageHelper<T> load(final String pageNum, final String pageSize, final Object... params) {
        final long loadPage = (null == pageNum || "".equals(pageNum.trim())) ? 1 : Long.valueOf(pageNum).longValue();
        final int itemsOnPage = (null == pageSize || "".equals(pageSize.trim())) ? DEFAULT_MAX_ITEMS_ON_PAGE : Integer.valueOf(pageSize).intValue();
        return new PageHelper<T>(finder, itemsOnPage, loadPage, params);
    }
    
    public PageHelper<T> load(final String pageNum, final Object... params) {
        final long loadPage = (null == pageNum || "".equals(pageNum.trim())) ? 1 : Long.valueOf(pageNum).longValue();
        return new PageHelper<T>(finder, maxItemsOnPage, loadPage, params);
    }

    public PageHelper<T> setItemsOnPage(final int itemsOnPage) {
        return new PageHelper<T>(finder, itemsOnPage, currentPage, params);
    }

    public List<T> getItems() {
        return items;
    }

    public PageInfo getPageInfo() {
        return pageInfo;
    }

    private static <T> List<T> initItems(final Finder<T> finder, final PageInfo pageInfo, final Object[] params) {
        if (false == pageInfo.getHasItems()) {
            return Collections.emptyList();
        }
        final List<T> items = finder.finder(pageInfo.getFirstItemIndex(), pageInfo.getMaxItemsOnPage(), params);
        return items;
    }

    private static PageInfo initPageInfo(final Finder finder, final Object[] params, final long currentPage, final int maxItemsOnPage) {
        //TODO: better names for countedItems and itemCount ; probably there is a need to change the design 
        final long countedItems = finder.counter(params);
        final boolean isCounterUsed = (countedItems > -1);
        final int firstResult = PageInfo.calculateFirstResult(currentPage, maxItemsOnPage);
        final long itemCount = (true == isCounterUsed) ? countedItems : finder.finder(firstResult, maxItemsOnPage + 1, params).size();
        return new PageInfo(maxItemsOnPage, itemCount, currentPage, isCounterUsed);
    }

    @Deprecated
    public void reset() {
    }

    public interface Finder<T> extends Serializable {

        long counter(Object... params);

        List<T> finder(int firstResult, int maxResult, Object... params);
    }
}
